﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web.Compilation;
using Dexter.Dependency.Installation;

namespace Dexter.Dependency {
	/// <summary>
	/// 	Inversion Of Controll Class controller
	/// </summary>
	public static class DexterContainer {
		static bool started;

		#region Overriding container for test

		/// <summary>
		/// 	Gets the engine.
		/// </summary>
		/// <value>The engine.</value>
		public static IDexterContainer Engine { get; private set; }

		public static void SetCurrent ( IDexterContainer newEngine ) {
			if ( Engine != null ) {
				Engine.Shutdown ( );
			}

			Engine = newEngine;
		}

		#endregion

		/// <summary>
		/// 	Starts up.
		/// </summary>
		public static void StartUp ( ) {
			if ( started ) {
				return;
			}

			try {
				ReadOnlyCollection <Assembly> dexterAssemblies = !IsHostedInAspnet ( )
				                                                 	? new ReadOnlyCollection <Assembly> ( LoadAssembliesFromPath ( Path.GetDirectoryName ( Assembly.GetEntryAssembly ( ).Location ) ).Where ( x => x.FullName.StartsWith ( "Dexter." ) ).ToList ( ) )
				                                                 	: new ReadOnlyCollection <Assembly> ( BuildManager.GetReferencedAssemblies ( ).Cast <Assembly> ( ).Where ( x => x.FullName.StartsWith ( "Dexter." )).ToList ( ) );

				Type containerType = dexterAssemblies.SelectMany ( x => x.GetTypes ( ) ).Single(t => !t.IsInterface && !t.IsAbstract && typeof ( IDexterContainerFactory ).IsAssignableFrom ( t ));

				var factory = Activator.CreateInstance ( containerType ) as IDexterContainerFactory;

				if ( factory == null ) {
					throw new ConfigurationErrorsException ( string.Format ( "The type {0} does not implement the IAlpContainerFactory interface." , containerType.FullName ) );
				}

				Engine = factory.Create ( );
				Engine.Register ( "IDexterContainer" , typeof ( IDexterContainer ) , Engine , LifeCycle.Singleton );
				Engine.Register ( "IDexterContainerRegister", typeof ( IDexterContainerRegister ), Engine, LifeCycle.Singleton );
				Engine.Register ( "IDexterContainerResolver" , typeof ( IDexterContainerResolver ) , Engine , LifeCycle.Singleton );

				var installer = factory.CreateInstaller ( );

				var coreInstaller = installer.GetInstallers ( dexterAssemblies );
				coreInstaller.ForEach ( x => x.ServiceRegistration ( Engine ) );
				coreInstaller.ForEach ( x => x.ServiceRegistrationComplete ( Engine ) );
				var isInstalled = Engine.Resolve <ISetupControl> ( ).IsInstalled;

				if ( isInstalled ) {
					coreInstaller.ForEach ( x => x.ApplicationStarted ( Engine ) );
				}

				started = true;
			}
			catch (Exception e) {
				if(Engine == null)
					return;

				Engine.Shutdown();
			}
		}

		static IEnumerable <Assembly> LoadAssembliesFromPath ( string path ) {
			var dlls = Directory.GetFiles ( path , "*.dll" );
			return dlls.Select ( asm => Assembly.Load ( Path.GetFileNameWithoutExtension ( asm ) ) ).ToList ( );
		}

		/// <summary>
		/// 	Shutdowns this instance.
		/// </summary>
		public static void Shutdown ( ) {
			Engine.Shutdown ( );
		}

		/// <summary>
		/// 	Configures the specified config.
		/// </summary>
		/// <param name = "config">The config.</param>
		public static void Configure ( string config ) {
			Engine.Configure ( config );
		}

		/// <summary>
		/// 	Resolves the specified type.
		/// </summary>
		/// <param name = "type">The type.</param>
		/// <returns></returns>
		public static object Resolve ( Type type ) {
			if ( type == null ) {
				throw new ArgumentNullException ( );
			}

			return Engine.Resolve ( type );
		}

		/// <summary>
		/// 	Resolves the specified type.
		/// </summary>
		/// <param name = "type">The type.</param>
		/// <param name = "key">The key.</param>
		/// <returns></returns>
		public static object Resolve ( Type type , string key ) {
			if ( type == null ) {
				throw new ArgumentNullException ( );
			}

			if ( key == null ) {
				throw new ArgumentNullException ( );
			}

			if ( key == string.Empty ) {
				throw new ArgumentException ( );
			}

			return Engine.Resolve ( type , key );
		}

		/// <summary>
		/// 	Resolves this instance.
		/// </summary>
		/// <typeparam name = "T"></typeparam>
		/// <returns></returns>
		public static T Resolve <T> ( ) {
			return Engine.Resolve <T> ( );
		}

		/// <summary>
		/// 	Resolves the specified key.
		/// </summary>
		/// <typeparam name = "T"></typeparam>
		/// <param name = "key">The key.</param>
		/// <returns></returns>
		public static T Resolve <T> ( string key ) {
			if ( key == null ) {
				throw new ArgumentNullException ( );
			}

			if ( key == string.Empty ) {
				throw new ArgumentException ( );
			}

			return Engine.Resolve <T> ( );
		}

		/// <summary>
		/// 	Registers the specified key.
		/// </summary>
		/// <param name = "key">The key.</param>
		/// <param name = "type">The type.</param>
		/// <param name = "implementedBy"></param>
		/// <param name = "lifeCycle">The life cycle.</param>
		public static void Register ( string key , Type type , Type implementedBy , LifeCycle lifeCycle ) {
			if ( key == null ) {
				throw new ArgumentNullException ( );
			}

			if ( key == string.Empty ) {
				throw new ArgumentException ( );
			}

			if ( type == null ) {
				throw new ArgumentNullException ( );
			}

			if ( implementedBy == null ) {
				throw new ArgumentNullException ( );
			}

			Engine.Register ( key , type , implementedBy , lifeCycle );
		}

		/// <summary>
		/// 	Registers the specified key.
		/// </summary>
		/// <typeparam name = "T"></typeparam>
		/// <typeparam name = "TK"></typeparam>
		/// <param name = "key">The key.</param>
		/// <param name = "lifeCycle">The life cycle.</param>
		public static void Register <T , TK> ( string key , LifeCycle lifeCycle ) where TK : class , T {
			if ( key == null ) {
				throw new ArgumentNullException ( );
			}

			if ( key == string.Empty ) {
				throw new ArgumentException ( );
			}

			Engine.Register <T , TK> ( key , lifeCycle );
		}

		/// <summary>
		/// 	Registers the components by base class.
		/// </summary>
		/// <typeparam name = "T"></typeparam>
		/// <param name = "assembly">The assembly.</param>
		/// <param name = "lifeCycle">The life cycle.</param>
		public static void RegisterComponentsByBaseClass <T> ( Assembly assembly , LifeCycle lifeCycle ) {
			if ( assembly == null ) {
				throw new ArgumentNullException ( );
			}

			Engine.RegisterComponentsByBaseClass <T> ( assembly , lifeCycle );
		}

		/// <summary>
		/// 	Resolves all.
		/// </summary>
		/// <typeparam name = "T"></typeparam>
		/// <returns></returns>
		public static T[] ResolveAll <T> ( ) {
			return Engine.ResolveAll <T> ( );
		}

		/// <summary>
		/// 	Resolves all.
		/// </summary>
		/// <param name = "type">The type.</param>
		/// <returns></returns>
		public static object[] ResolveAll ( Type type ) {
			if ( type == null ) {
				throw new ArgumentNullException ( );
			}

			return Engine.ResolveAll ( type );
		}

		static bool IsHostedInAspnet ( ) {
			return ( AppDomain.CurrentDomain.GetData ( ".appDomain" ) != null );
		}
	}
}